package com.ruoyi.common.utils.http;

import java.io.*;
import java.util.UUID;
import java.net.HttpURLConnection;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import com.alibaba.fastjson2.JSONException;
import com.ruoyi.common.exception.ApiException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.MessageDigest;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.util.UriComponentsBuilder;


/**
 * 通用http发送方法
 * 
 * @author ruoyi
 */
public class HttpUtils
{
    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);


    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url 发送请求的 URL
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url)
    {
        return sendGet(url, StringUtils.EMPTY);
    }

    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param)
    {
        return sendGet(url, param, Constants.UTF8);
    }

    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @param contentType 编码类型
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param, String contentType)
    {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try
        {
            String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
            log.info("sendGet - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection connection = realUrl.openConnection();
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
            String line;
            while ((line = in.readLine()) != null)
            {
                result.append(line);
            }
            log.info("recv - {}", result);
        }
        catch (ConnectException e)
        {
            log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
        }
        finally
        {
            try
            {
                if (in != null)
                {
                    in.close();
                }
            }
            catch (Exception ex)
            {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param)
    {
        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try
        {
            log.info("sendPost - {}", url);
            URL realUrl = new URL(url);
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            out.print(param);
            out.flush();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
            String line;
            while ((line = in.readLine()) != null)
            {
                result.append(line);
            }
            log.info("recv - {}", result);
        }
        catch (ConnectException e)
        {
            log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
        }
        finally
        {
            try
            {
                if (out != null)
                {
                    out.close();
                }
                if (in != null)
                {
                    in.close();
                }
            }
            catch (IOException ex)
            {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    public static String sendSSLPost(String url, String param)
    {
        StringBuilder result = new StringBuilder();
        String urlNameString = url + "?" + param;
        try
        {
            log.info("sendSSLPost - {}", urlNameString);
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
            URL console = new URL(urlNameString);
            HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setDoOutput(true);
            conn.setDoInput(true);

            conn.setSSLSocketFactory(sc.getSocketFactory());
            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
            conn.connect();
            InputStream is = conn.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String ret = "";
            while ((ret = br.readLine()) != null)
            {
                if (ret != null && !"".equals(ret.trim()))
                {
                    result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
                }
            }
            log.info("recv - {}", result);
            conn.disconnect();
            br.close();
        }
        catch (ConnectException e)
        {
            log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
        }
        return result.toString();
    }

    private static class TrustAnyTrustManager implements X509TrustManager
    {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType)
        {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType)
        {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers()
        {
            return new X509Certificate[] {};
        }
    }

    private static class TrustAnyHostnameVerifier implements HostnameVerifier
    {
        @Override
        public boolean verify(String hostname, SSLSession session)
        {
            return true;
        }
    }


    // ##############################################################################################################
    // HTTP 请求 API 需要的 签名信息生成方法、重载的GET、Post请求方法以及通用API调用方法

    /**
     * 生成签名信息
     *
     * @param appId     appId
     * @param noncestr  随机字符串
     * @param timestamp 时间戳
     * @param appKey    appKey
     * @return 签名信息
     */
    public static String generateSign(String appId, String noncestr, String timestamp, String appKey) {
        try {
            // 步骤一：拼接 appId, noncestr, timestamp
            String stringA = String.format("appId=%s&noncestr=%s&timestamp=%s", appId, noncestr, timestamp);

            // 步骤二：拼接 appKey
            String strSignTemp = stringA + "&key=" + appKey;

            // 步骤三：生成 MD5 签名
            // 获取 MD5 实例
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 将 strSignTemp 转换为字节数组（使用 UTF-8 编码），并传递给 MessageDigest 实例
            md.update(strSignTemp.getBytes(StandardCharsets.UTF_8));
            // 调用 digest 方法，生成 MD5 摘要（一个 16 字节的数组）
            byte[] digest = md.digest();
            StringBuilder sign = new StringBuilder();
            for (byte b : digest) {
                sign.append(String.format("%02x", b & 0xff));
            }

            // 测试 检查sign
            log.info("sign.toString():"+sign.toString());

            // 返回生成的 32 位小写 MD5 签名
            return sign.toString();
        } catch (Exception e) {
            throw new RuntimeException("生成签名失败", e);
        }
    }

    /**
     * 生成随机字符串 (32位)
     */
    public static String generateNonceStr() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 生成时间戳 (10位)
     */
    public static String generateTimestamp() {
        return String.valueOf(System.currentTimeMillis() / 1000);
    }

    /**
     * 构建认证请求头 X-Hw-ApiAuth
     *
     * @param appId  appId
     * @param appKey appKey
     * @return 认证请求头 JSON 字符串
     */
    public static String buildApiAuthHeader(String appId, String appKey) {

        System.out.println("appId:"+appId);
        System.out.println("appKey:"+appKey);

        String noncestr = generateNonceStr();
        String timestamp = generateTimestamp();
        String sign = generateSign(appId, noncestr, timestamp, appKey);

        JSONObject authHeader = new JSONObject();
        authHeader.put("appId", appId);
        authHeader.put("noncestr", noncestr);
        authHeader.put("timestamp", timestamp);
        authHeader.put("sign", sign);

        return authHeader.toJSONString();
    }

    /**
     * 向指定 URL 发送 GET 请求
     *
     * @param url     请求 URL（包含查询参数）
     * @param apiAuth X-Hw-ApiAuth 请求头
     * @return 响应结果
     */
    public static String sendGetDC(String url, String apiAuth,String contentType) {
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            log.info("请求 URL: {}", url);
            log.info("认证请求头 (X-Hw-ApiAuth): {}", apiAuth);

            URL realUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();

            // 设置请求头
            conn.setRequestProperty("X-Hw-ApiAuth", apiAuth);
            conn.setRequestProperty("Content-Type", contentType);
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");

            // 设置请求方法
            conn.setRequestMethod("GET");
            conn.setDoOutput(false);
            conn.setDoInput(true);

            // 获取响应状态码
            int responseCode = conn.getResponseCode();
            log.info("响应状态码: {}", responseCode);

            // 读取响应
            if (responseCode == HttpURLConnection.HTTP_OK) {
                in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
            } else {
                // 读取错误响应体
                in = new BufferedReader(new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8));
            }

            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }

            log.info("API 响应: {}", result.toString());
        } catch (Exception e) {
            log.error("发送 GET 请求异常: ", e);
            throw new RuntimeException("发送 GET 请求异常", e);
        } finally {
            try {
                if (in != null) in.close();
            } catch (IOException ex) {
                log.error("关闭流异常: ", ex);
            }
        }
        return result.toString();
    }

    /**
     * 向指定 URL 发送 POST 请求
     *
     * @param url        请求 URL
     * @param params      请求参数（form-data 格式）
     * @param apiAuth    X-Hw-ApiAuth 请求头
     * @param contentType 请求体类型（如 "application/x-www-form-urlencoded" 或 "application/json"）
     * @return 响应结果
     */
    public static String sendPostDC(String url, String params, String apiAuth, String contentType) {
        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            log.info("请求 URL: {}", url);
            log.info("请求参数 params: {}", params);
            log.info("认证请求头 (X-Hw-ApiAuth): {}", apiAuth);
            log.info("Content-Type: {}", contentType);
            // 处理请求地址
            URL realUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
            // 设置请求头
            conn.setRequestProperty("X-Hw-ApiAuth", apiAuth);
            conn.setRequestProperty("Content-Type", contentType);
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
            // 设置请求方法
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 发送请求参数
            out = new PrintWriter(conn.getOutputStream());
            out.print(params);
            out.flush();

            // 获取响应状态码
            int responseCode = conn.getResponseCode();
            log.info("响应状态码: {}", responseCode);

            // 读取响应
            if (responseCode == HttpURLConnection.HTTP_OK) {
                in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
            } else {
                // 读取错误响应体
                in = new BufferedReader(new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8));
            }

            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }

            // 日志记录 API 响应
            log.info("API 响应结果: {}", result);
        } catch (Exception e) {
            log.error("发送 POST 请求异常: ", e);
            throw new RuntimeException("发送 POST 请求异常", e);
        } finally {
            try {
                if (out != null) out.close();
                if (in != null) in.close();
            } catch (IOException ex) {
                log.error("关闭流异常: ", ex);
            }
        }
        return result.toString();
    }

    /**
     * 汇文API 通用调用方法 请求方式Post
     * 请求体类型 如果接口同时支持get和post，在调用接口的时候使用post方式那么body选择form-data，如果body需要传json格式具体接口说明会写上头消息Content-Type=application/json。
     * @param targetURL   接口地址
     * @param params      请求参数（form-data 格式）
     */
    public static JSONObject callAPI(String targetURL, JSONObject params, String apiAuth, String contentType) {
        // 参数校验
        if (targetURL == null || targetURL.trim().isEmpty()) {
            throw new IllegalArgumentException("目标路径不能为空");
        }
        if (params == null) {
            throw new IllegalArgumentException("params不能为空");
        }
        // 设置请求参数的内容类型（Content-Type）
        // 注意：HTML表单默认使用 'application/x-www-form-urlencoded' 作为 Content-Type。
        // 当API明确要求传递JSON格式的参数时，需要指定 Content-Type 为 'application/json;charset=utf-8'。
        String paramsToString=null;
        if (contentType == null || contentType.trim().isEmpty()) {
            // 如果 Content-Type 未设置（即为 null 或空字符串），则为其设置一个默认值。
            contentType = "application/x-www-form-urlencoded";
            // JSON参数格式转换 将 JSONObject 转换为适合表单提交的查询参数字符串
            paramsToString=jsonToParams(params);
        }else {
            paramsToString=params.toString();
        }
        String response=null;
        try {
            // 发送 POST 请求
            response= HttpUtils.sendPostDC(targetURL, paramsToString, apiAuth, contentType);
            // 查看返回对象
            log.info("response:{}",response);
            if (response == null || response.trim().isEmpty()) {
                log.error("The response from the server was empty or null. Current parameters:{}",paramsToString);
                return null;
            }
            // 返回响应对象中的 data
            return JSONObject.parseObject(response).getJSONObject("data");
            // 直接返回整个对象
            // return JSONObject.parseObject(response);

        } catch (HttpClientErrorException e) {
            log.error("HTTP客户端错误: 状态码={}, 响应={}", e.getStatusCode(), e.getResponseBodyAsString());
            throw new ApiException("API请求失败: " + e.getMessage(), e);
        } catch (JSONException e) {
            log.error("JSON解析失败: 原始响应={}", response);
            throw new ApiException("响应数据格式错误", e);
        } catch (Exception e) {
            log.error("未知异常: ", e);
            throw new ApiException("系统内部错误", e);
        }
    }

    /**
     * 将 JSONObject 转换为适合表单提交的查询参数字符串 JSONObject ==> id=123&type=user&name=张三
     * @param params    包含键值对的 JSONObject
     * @return          查询参数字符串
     */
    public static String jsonToParams(JSONObject params) {
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        params.forEach((key, value) -> map.add(key, value.toString()));
        return UriComponentsBuilder.newInstance().queryParams(map).build().getQuery();
    }

}